local oo = require("SharedLibs.BaseLib")

Debugger = require("SharedLibs.Debugger");
ISignal = require("SharedLibs.Signal.ISignal");
IEmitter = require("SharedLibs.Signal.IEmitter");
TU = require("SharedLibs.Utils.TableUtil");

_IsStudio = Debugger.IsStudio;

--- 注意：IEmitter支持多参数Emit
--- 例如，可以 signal:Emit(1, "a")，那么对应的Wait()返回值也是多参数的。
local Signal = oo.class();
Signal:implements(ISignal);
Signal:implements(IEmitter);

---
function Signal:constructor(name)
    self.name = name or "Signal";

    self._handlers = {};
    --- handler count
    self._count = 0;
    --- 用来保存handler的slot的数量。有的slot可能是null（Emit中调用了Off）
    self._slotCount = 0;

    self._nEmit = 0;

    --- Dead handlers map. 如果在inEmit中调用了Off/Clear, 就必须放到这个Map里(下次Emit前会根据这个Map清理handler).
    --- 如果Emit中直接清理handler, 会导致Off/Clear产生的效果是不确定的(依赖于handler的顺序), 是不对的!
    self._DHs = nil;
end

function Signal:Emit(...)
    --- 移除在触发中 (_inEmit < 0) Off/Clear 掉的 dead handler
    self:_delDHs();

    self._nEmit = self._nEmit + 1;

    local arr = self._handlers;
    local n = self._slotCount;
    for i = 1, n do
        local f = arr[i];
        if f then f(...); end
    end
    self._nEmit = self._nEmit - 1;
end

--- retDestroyer: 是否返回一个destroyer函数用来Off
--- 如果为true, 那么直接调用destroyer()即可取消注册
--- 注意: 尽量用 signal:Wait 来监听signal而不是直接On/Off. 
--- On/Off的形式如果没有配对使用, 会导致内存泄漏, 而 Wait 永远不会导致这个问题.
function Signal:On(handler, retDestroyer)
    if _IsStudio then
        assert(handler);
        Debugger.Assert(not self:HasHandler(handler));
    end

    if self._DHs and self._DHs[handler] then
        self._count = self._count + 1;
        --- 如果在dead列表内, 说明handler尚未移除, 那么只要清除Dead列表里的标志即可.
        self._DHs[handler] = nil;
    else
        self._slotCount = self._slotCount + 1;
        self._count = self._count + 1;
        self._handlers[self._slotCount] = handler;
    
        if _IsStudio then
            if self._count > 300 then
                warn("Too many handler count: " .. self._count .. ", name: " ..
                         self.name);
            end
        end
    end

    if retDestroyer then return function() self:Off(handler); end end
end

function Signal:Off(handler)
    assert(handler);
	if self._count == 0 then return end

    if self._nEmit > 0 then
        local dhs = self._DHs;
        if dhs then
            --- 已经被删除过了
            if dhs[handler] then
                if _IsStudio then
                    warn("handler already in deadHandler list, repeated Signal:Off ?");                    
                end
                return;
            end
        else
            self._DHs = {}
        end

        --if _IsStudio then assert(not self._DHs[handler]); end
        self._DHs[handler] = true;
        self._count = self._count - 1;
    else
        self:_delDHs();

        local arr = self._handlers;
        local tail = self._slotCount;
        local i = self._slotCount;
        local found = false;
        while i > 0 do
            local cur = arr[i];

            if cur == handler then
                if i == tail then
                    arr[tail] = nil;
                else
                    --- 和tail交换，并且tail减一
                    arr[i] = arr[tail];
                    arr[tail] = nil;
                end
                tail = tail - 1;
                found = true;
                self._count = self._count - 1;
                break
            end
            i = i - 1;
        end
        if not found then
            warn("Signal.Off() failed, handler not found! Stack: " ..
                     debug.traceback());
        end

        self._slotCount = tail;
        assert(self._slotCount == self._count);
    end
end

function Signal:HasHandler(handler)
    if self._DHs then if self._DHs[handler] then return false; end end

    local arr = self._handlers;
    for i = 1, self._slotCount do
        local f = arr[i];
        if f == handler then return true; end
    end
end

function Signal:GetHandlerCount() return self._count; end

--[[
    注意: Clear是一个非常危险的操作, 例如某个co可能正在等待某个Signal, 然后你在某处直接调用了Signal的Clear().
    这会导致那个co永远等待下去.
]]
function Signal:Clear()
    self._count = 0;

    if self._nEmit > 0 then
        if self._DHs == nil then self._DHs = {} end

        --- 全部放入DHs里
        local dHS = self._DHs;
        local arr = self._handlers;
        for i = 1, self._slotCount do
            local f = arr[i];
            if f then dHS[f] = true; end
        end
    else
        for handler in pairs(self._handlers) do
            self._handlers[handler] = nil;
        end
        self._slotCount = 0;
    end
end

function Signal:_delDHs()
    local dHs = self._DHs;
    if not dHs then return end

    local arr = self._handlers;
    local tail = self._slotCount;
    local i = self._slotCount;

    local deadCount = 0;
    local delCount = 0;
    if _IsStudio then deadCount = TU.GetTableSize(dHs); end

    while i > 0 do
        local cur = arr[i];

        if cur and dHs[cur] then
            if i == tail then
                arr[tail] = nil;
            else
                --- 和tail交换
                arr[i] = arr[tail];
                arr[tail] = nil;
            end
            tail = tail - 1;
            delCount = delCount + 1;
        end
        i = i - 1;
    end

    if _IsStudio then
        if delCount ~= deadCount then
            warn("DelCount: " .. delCount .. " , deadCount: " .. deadCount);
        end
        assert(delCount == deadCount);
    end

    self._DHs = nil;
    self._slotCount = tail;
    assert(self._slotCount == self._count);
end

return Signal;
